#pragma once
#include <memory>
#include <vector>
#include "ErrorCodes.h"

namespace DXBase
{
	template <class T>
	ref class DataIterator sealed : Windows::Foundation::Collections::IIterator<T>
	{
		Windows::Foundation::Collections::IVector<T>^ target;
		unsigned int index;
	public:
		DataIterator(Windows::Foundation::Collections::IVector<T>^ target) { this->target = target; }
		virtual unsigned int GetMany(Platform::WriteOnlyArray<T>^ items) { return target->GetMany(index, items); }
		virtual bool MoveNext() { return ++index < target->Size; }
		virtual property T Current { T get() { return target->GetAt(index); } }
		virtual property bool HasCurrent { bool get() { return index < target->Size; } }
	};

	template <class T>
	ref class DataView sealed : Windows::Foundation::Collections::IVectorView<T>
	{
		Windows::Foundation::Collections::IVector<T>^ target;
	public:
		DataView(Windows::Foundation::Collections::IVector<T>^ target) { this->target = target; }
		virtual T GetAt(unsigned int index) { return target->GetAt(index); }
		virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray<T>^ items) { return target->GetMany(startIndex, items); }
		virtual bool IndexOf(T value, unsigned int *index) { return target->IndexOf(value, index); }
		virtual property unsigned int Size { unsigned int get() { return target->Size; } }
		virtual Windows::Foundation::Collections::IIterator<T>^ First() { return ref new DataIterator<T>(target); }
	};

	// unsafe data, no bounds checking!
	template <class T>
	ref class DataArray sealed : Windows::Foundation::Collections::IVector<T>
	{
	public:
		DataArray() { }
		DataArray(unsigned int originalSize) { data.resize(originalSize); }

		virtual void Append(T value) 
		{
			if (fixedSize)
				throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
			data.push_back(value); 
		}
		virtual void Clear() 
		{
			if (fixedSize)
				throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
			data.clear(); 
		}
		virtual T GetAt(unsigned int index) { return data[index]; }
		virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray<T>^ items) 
		{
			int N = max(min(this->Size-startIndex, items->Length), 0);
			auto start = data.begin() + startIndex;
			for (int i=0; i<N; i++)
				items[i] = *start++;
			return N;
		}
		virtual Windows::Foundation::Collections::IVectorView<T>^ GetView() { return ref new DataView<T>(this); }
		virtual bool IndexOf(T value, unsigned int *index) 
		{
			for (int i = 0, N = Size; i < N; i++)
			{
				if (memcmp(&data[i], &value, sizeof(T)) == 0)
				{
					if (index) 
						*index = i;
					return true;
				}
			}
			return false; 
		}
		virtual void InsertAt(unsigned int index, T value) 
		{
			if (fixedSize)
				throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
			data.insert(data.begin()+index, value); 
		}
		virtual void RemoveAt(unsigned int index) 
		{
			if (fixedSize)
				throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
			data.erase(data.begin()+index); 
		}
		virtual void RemoveAtEnd() 
		{
			if (fixedSize)
				throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
			data.pop_back(); 
		}
		virtual void ReplaceAll(const Platform::Array<T>^ items) 
		{
			int N = min(this->Size, items->Length);
			for (int i = 0; i < N; i++)
				data[i] = items[i];
		}
		virtual void SetAt(unsigned int index, T value) { data[index] = value; }
		//virtual unsigned int getsize() = Windows::Foundation::Collections::IVector<T>::Size::get { return data.size(); }
		// get or set the size of the array
		virtual property uint32 Size { uint32 get() { return (uint32)data.size(); } }
		virtual Windows::Foundation::Collections::IIterator<T>^ First() { return ref new DataIterator<T>(this); }

		void Resize(unsigned int value) { data.resize(value); }
		// when it is fixed size the IVector<T> method that resizes it fails

		void Append(DataArray<T>^ other)
		{
			if (other)
				Append(other->data);
		}

	internal:
		bool fixedSize;
		std::vector<T> data;

		void Append(std::vector<T>& other)
		{
			data.insert(data.end(), other.begin(), other.end());
		}
	};


	template <class T>
	ref class DataPointerView sealed : Windows::Foundation::Collections::IVector<T>
	{
		UINT size;
		std::function<T(UINT)> getvalue;
		std::function<void(UINT,T)> setvalue;

	internal:
		DataPointerView(UINT size, std::function<T(UINT)> getvalue, std::function<void(UINT,T)> setvalue) 
			: size(size), getvalue(getvalue), setvalue(setvalue)
		{ 
		}
		void Initialize(UINT size, std::function<T(UINT)> getvalue, std::function<void(UINT,T)> setvalue)
		{
			this->size = size;
			this->getvalue = getvalue;
			this->setvalue = setvalue;
		}

	public:
		DataPointerView() {}

		virtual void Append(T value) 
		{
			throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
		}
		virtual void Clear() 
		{
			throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
		}
		virtual T GetAt(unsigned int index) 
		{
			if (index >= size)
				throw ref new Platform::InvalidArgumentException();
			return getvalue(index); 
		}
		virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray<T>^ items) 
		{
			int N = max(min(this->Size-startIndex, items->Length), 0);
			for (int i=0; i<N; i++)
				items[i] = getvalue(i + startIndex);
			return N;
		}
		virtual Windows::Foundation::Collections::IVectorView<T>^ GetView() { return ref new DataView<T>(this); }
		virtual bool IndexOf(T value, unsigned int *index) 
		{
			for (int i = 0, N = Size; i < N; i++)
			{
				T t = getvalue(i);
				if (memcmp(&t, &value, sizeof(T)) == 0)
				{
					if (index) 
						*index = i;
					return true;
				}
			}
			return false; 
		}
		virtual void InsertAt(unsigned int index, T value) 
		{
			throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
		}
		virtual void RemoveAt(unsigned int index) 
		{
			throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
		}
		virtual void RemoveAtEnd() 
		{
			throw ExHelper::CreateException(ErrorCodes::FixedSizeArray);
		}
		virtual void ReplaceAll(const Platform::Array<T>^ items) 
		{
			if (setvalue._Empty())
				return;
			int N = min(this->Size, items->Length);
			for (int i = 0; i < N; i++)
				setvalue(i, items[i]);
		}
		virtual void SetAt(unsigned int index, T value)
		{
			if (index >= size)
				throw ref new Platform::InvalidArgumentException();
			setvalue(index, value); 
		}
		//virtual unsigned int getsize() = Windows::Foundation::Collections::IVector<T>::Size::get { return data.size(); }
		// get or set the size of the array
		virtual property uint32 Size { uint32 get() { return size; } }
		virtual Windows::Foundation::Collections::IIterator<T>^ First() { return ref new DataIterator<T>(this); }
	};

}

